#ifndef __MTD_EXTENSIONS_GEODB__
#define __MTD_EXTENSIONS_GEODB__

struct hash_node_entry {
    struct in6_addr attacker_ip;
    struct in6_addr victim_ip;
    u_int64_t byte_count;
    u_int32_t attack_type;
    u_int32_t app_id;
    u_int32_t packet_count;
    u_int32_t session_count;
    char attacker_country_code[3];
    char victim_country_code[3];
    struct hash_node_entry *next;
};

struct ipv4_entry {
    char country_code[3];
    u_int32_t range_start;
    u_int32_t range_end;
};

struct ipv6_entry {
    char country_code[3];
    struct in6_addr range_start;
    struct in6_addr range_end;
};

struct geodb {
    int continent_size;
    int continent_use;
    int ipv4_size;
    int ipv4_use;
    int ipv6_size;
    int ipv6_use;
    struct continent_entry *p_continent_table;
    struct ipv4_entry *p_ipv4_table;
    struct ipv6_entry *p_ipv6_table;
};

#define HASH_TABLE_SIZE 1027
struct hash_table {
    int hash_table_count;
    struct hash_node_entry *hash_table[HASH_TABLE_SIZE];
};

/*
 * 한 thread는 계속 로그를 받아들여서 그 로그의 정보를 hash table에 쌓기만 한다.
 * 다른 thread는 10초마다 한번씩 hash table의 내용을 정리해서 DB에 비워낸다.
 * 쌓는 동작은 오래 걸리지 않고 비우는 동작이 오래 걸리므로 hash table에 대한
 * 컨트롤은 비우는 쪽이 주도한다. 비우는 쪽이 테이블을 swap하고 비우기 시작한다.
 * 10초마다 비우는 동작이 수행된다.
 */
struct hash_tables {
    struct hash_table table1;
    struct hash_table table2;
    struct hash_table *p_table_stacking;
    struct hash_table *p_table_flushing;
    pthread_mutex_t hash_mutex;
};

/* hash table related functions */
int init_hash_tables(void);
void swap_table(void);
void *flush_geodb_table(void *p_args);

int comp_ipv6(struct in6_addr *pA, struct in6_addr *pB);
int comp_nodes(struct hash_node_entry *p_A, struct hash_node_entry *p_B);
int extract_info_from_ips_detect(struct hash_node_entry *p_node, struct logf_act_ips_detect_3 *p_log);
int calc_hash(struct hash_node_entry *p_node);
int add_value_to_hash_node(struct hash_node_entry *p_hash_node, struct hash_node_entry *p_insert_node);
int insert_entry_to_hash(struct hash_node_entry **pp_node);
int init_geodb_statistics_ips_detect(struct geodb *pGeoDB);
int update_geodb_statistics_ips_detect(void *p_log, size_t log_size);

/* GeoDB related functions */
int add_entry_to_continent(struct geodb *pGeoDB, char *pCountry, char *pContinent);
int add_entry_to_ipv4(struct geodb *pGeoDB, char *pRangeStart, char *pRangeEnd, char *pCountry);
int add_entry_to_ipv6(struct geodb *pGeoDB, char *pRangeStart, char *pRangeEnd, char *pCountry);
int load_continent_info(struct geodb *pGeoDB);
int load_ipv4_geoip_table(struct geodb *pGeoDB);
int load_ipv6_geoip_table(struct geodb *pGeoDB);
int lookup_ipv4_table_with_string(struct geodb *pGeoDB, char *target, char *country_code, int len);
int lookup_ipv6_table_with_string(struct geodb *pGeoDB, char *target, char *country_code, int len);
int lookup_ipv4_table_with_string_linear(struct geodb *pGeoDB, char *target, char *country_code, int len);
int lookup_ipv6_table_with_string_linear(struct geodb *pGeoDB, char *target, char *country_code, int len);
int lookup_ipv4_table(struct geodb *pGeoDB, u_int32_t ipv4_addr, char *country_code, int len);
int lookup_ipv6_table(struct geodb *pGeoDB, struct in6_addr *p_ipv6_addr, char *country_code, int len);

/* misc functions */
int leave_parsing_fail_log(int line_num, char *line, int column, int source_line, const char *message);

/* not ready */
int common_table_parser(char *filename, 
                        int (*parser_func)(struct geodb *pGeoDB, char *pRangeStart, 
                                           char *pRangeEnd, char *pCountry));

#endif
